package net.wangds.procengine.flow.instance;

import lombok.Getter;
import lombok.Setter;
import net.wangds.log.helper.LogHelper;
import net.wangds.procengine.ProcResEnum;
import net.wangds.procengine.flow.FlowContext;
import net.wangds.procengine.flow.FlowEngine;
import net.wangds.procengine.flow.define.FlowDef;
import net.wangds.procengine.flow.define.actor.ActorDef;
import net.wangds.procengine.flow.define.node.FlowNode;
import net.wangds.procengine.flow.define.node.FlowNodeTypeEnum;
import net.wangds.procengine.flow.define.node.StartFlowNode;
import net.wangds.procengine.flow.instance.actor.Actor;
import net.wangds.procengine.flow.instance.step.FlowStep;

import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;

/**
 * 简单流程实例模型.
 * @param <C> 上下文.
 */
public class SimpleFlowInstance<C extends FlowContext, A extends ActorDef> implements FlowInstance<C, A> {

    @Getter @Setter
    private String id;

    @Getter @Setter
    private String flowDefId;

    @Getter @Setter
    private Actor owner;

    @Getter @Setter
    private FlowStep<C> currentStep;

    @Getter @Setter
    private C context;

    @Getter @Setter
    private FlowDef<A> flowDef;

    @Override
    public ProcResEnum start() {
        //LogHelper.dev("----------------simple flow instance start");
        Optional<FlowNode> startNodeOp = findStartNode();
        //LogHelper.dev("----------------simple flow instance start :"+startNodeOp.isPresent());
        try {
            if (startNodeOp.isPresent()) {
                FlowNode startNode = startNodeOp.get();
                FlowEngine.run(this.getContext(), this.getOwner(), this, startNode);
                return this.currentStep.getRes();
            } else {
                LogHelper.debug("没有找到开始节点");
            }
        }catch(Throwable th){
            BiConsumer<FlowContext, Throwable> funcOnError = flowDef.getOnError();
            if(funcOnError==null){
                funcOnError=(c,t)->LogHelper.error(t);
            }
            funcOnError.accept(this.getContext(), th);
        }finally {
            BiConsumer<FlowContext, Actor> funcHouseKeeping = flowDef.getOnHouseKeeping();
            if(funcHouseKeeping!=null){
                funcHouseKeeping.accept(this.getContext(), this.getOwner());
            }
        }

        return ProcResEnum.FINISH;
    }

    protected Optional<FlowNode> findStartNode(){
        if(this.flowDef==null){
            return Optional.empty();
        }

        List<FlowNode> nodes = this.flowDef.getFlowNodes();
        if(nodes==null){
            return Optional.empty();
        }

        for(FlowNode node: nodes){
            if(FlowNodeTypeEnum.START==node.getType()){
                return Optional.of(node);
            }
        }

        if(!nodes.isEmpty()){
            StartFlowNode res = new StartFlowNode();
            FlowNode first = nodes.get(0);
            res.setNextId(first.getId());
            return Optional.of(res);
        }

        return Optional.empty();
    }
}
